'''
>>> from cStringIO import StringIO
>>> fragment_shader = FragmentShader(StringIO('// Fragment program\\nvarying vec3 pos;\\nvoid main() {\\n  gl_FragColor.rgb = pos.xyz;\\n}\\n'))
>>> vertex_shader = VertexShader(StringIO('// Vertex program\\nvarying vec3 pos;\\nvoid main() {\\n  pos = gl_Vertex.xyz;\\n  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\\n}\\n'))
>>> program = Program()
>>> program.attach((vertex_shader, fragment_shader))
>>> program.bind()
'''

if __name__ == '__main__':
  # if testing, have to import and init pygame before importing opengl
  import pygame
  from pygame.locals import *
  pygame.init()
  pygame.display.set_mode((800, 600), OPENGL | DOUBLEBUF)

from OpenGL.GL import *


class Shader(object):

  def __init__(self, type, file):
    if isinstance(file, str):
      file = open(file)
    self.type = type
    self.index = glCreateShader(type)
    self.name = hasattr(file, 'name') and file.name or repr(file)
    glShaderSource(self.index, file.read())
    glCompileShader(self.index)

  def __del__(self):
    glDeleteShader(self.index)

  def __str__(self):
    return 'Shader(%d:"%s")' % (self.index, self.filename)


class FragmentShader(Shader):

  def __init__(self, file):
    super(FragmentShader, self).__init__(GL_FRAGMENT_SHADER, file)

  def __str__(self):
    return 'FragmentShader(%d:"%s")' % (self.index, self.filename)


class VertexShader(Shader):

  def __init__(self, file):
    super(VertexShader, self).__init__(GL_VERTEX_SHADER, file)

  def __str__(self):
    return 'VertexShader(%d:"%s")' % (self.index, self.filename)


class Program(object):


  def __init__(self):
    self.index = glCreateProgram()

  def attach(self, shaders):
    if not hasattr(shaders, '__getitem__'):
      shaders = [shaders]
    for shader in shaders:
      glAttachShader(self.index, shader.index)
    glLinkProgram(self.index)

  def bind(self):
    glUseProgram(self.index)

  def unbind(self):
    glUseProgram(0)

if __name__ == '__main__':
  import doctest
  doctest.testmod()
